4.5 结构体
结构体这个概念在很多语言中都是存在的,例如C
、C++
中都有这样的概念,也类似于Java
中的类。
在实际的应用开发中,基本我们是避不开结构体的,而且结构体的相关概念及知识也是很多的。
本着本书为初学者的原则,我们不做深入的讨论,本节我们只讲解结构体的实际应用。
本节代码存放目录为 lesson10
什么是结构体?
Go
语言的很多概念其实都可以从字面意思理解,结构体看起来就是一个结构,那么从这里我们就可以联想到结构体可能包含多个东西的。
下面我们将以一个例子来做说明。
今天班级需要统计全班30个同学的个人信息,需要统计的信息包括:姓名、性别、年龄。
那么基于之前学习的内容,我们应该怎么去做呢?
我们可以有两种方式,第一种方式是为每个同学各定义一个姓名变量、性别变量、年龄变量,那么这样一共就会产生90个变量。大概是下面这样子:
var 1name, 2name, 3name string
var 1sex, 2sex, 3sex string
var 1age, 2age, 3age int
另一种方式就是使用map
,先通过数组将姓名全部存储起来,之后使用两个map
,姓名作为map
的key
值,value
值就是性别、年龄。大概实现方式下面这样:
var nameSlice = []string{"小明", "小红"...}
sexMap["小明"] = "女"
ageMap["小红"] = 11
从上面我们可以看出,这样统计是很复杂的。
那么从我们的常用方式来做,我们可能会使用一个表格,一个同学占用一行,每列分别是:姓名、性别、年龄。
那么如果我们把每一行作为一个变量,最终将这些变量都放到一个切片中,那是不是就可以更简单了呢?这样这个切片其实也就跟表格是一样的效果了。
结构体其实也就是像我们上面说的那样,使用一个变量,存储个体的信息。
定义结构体
上面我们讲到了统计同学的信息,那么接下来我们也以同学的信息来做说明。
结构体的定义规则如下:
type structName struct {
field fieldType
...
}
在上面的示例中,type
是固定的。
structName
是结构体的名称,比如我们这一次的例子,那么就是student
。
field
表示字段名称,我们可以定义为:name
,表示学生姓名。
fieldType
表示字段类型,我们可以定义为string
,表示学生姓名的数据类型。
那么同样的,我们就可以添加sex
、age
,如下所示:
type student struct {
Name string
Sex string
Age int
}
在上面的例子中,我们通过一个student
的结构体,就将学生的姓名、性别、年龄包含在一起了,那么我们在统计的时候,就可以将学生的信息都存储在这一个变量中。
需要注意的是,结构体同样适用于大写可导出、小写不可导出
的原则的,当结构体名称及字段的首字母为小写时,那么在不同的包里面就不可以被引用。
声明结构体
在上面我们已经讲到了结构体的定义,那么我们应该如何声明使用呢?
结构体的声明其实与之前我们学习到的基本数据类型声明没有太大的区别,不同的是,现在student
就相当于是int
、string
等类型了。
那么按照我们之前所学的,根据var varName varType
就可以得出一下结论:
var st student
我们还可以这样:
st := student{}
同时也可以这样:
st1 := new(student)
赋值及修改
上面我们讲到了结构体的声明,那么我们应该怎样去修改name
、sex
等值呢?
在Go
中,我们通过符号:.
就可以直接访问。如下所示:
var st student
st.Name = "小明"
st.Sex = "男"
st.Age = 11
如上面的代码所示,我们声明变量st
后,我们就可以通过st.Name
直接拿到我们定义的字段,那么同时就可以直接使用=
对字段进行赋值。
通过前面的学习我们可以知道,所有变量都是可以在声明的时候进行赋值的。在结构体方面,我们可以这样:
st1 := student{
Name: "小红",
Sex: "女",
Age: 1,
}
fmt.Printf("%#v\n", st1)
在上面的代码中,我们在声明的同时就为结构体的字段进行了赋值,同时我们使用%#v
将结构体的值进行了输出。
结果输出如下:
main.student{Name:"小明", Sex:"男", Age:11}
main.student{Name:"小红", Sex:"女", Age:1}
内嵌与嵌套
现在,我们还需要同时统计同学们的成绩,那么我们应该怎么做呢?
正常来说,我们可以通过增加字段的方式实现,比如在student
中增加shuxue
、yuwen
。
但是这样的话,如果科目太多的话,管理起来就比较混乱,所以我们可以使用嵌套来实现。代码如下:
type xueKe struct {
ShuXue int
YuWen int
}
type student struct {
Name string
Sex string
Age int
xueKe xueKe
}
在上面的代码中,我们新增了一个结构体xueKe
,用于存储学生的成绩。
同时我们在student
中增加了一个字段叫做XueKe
,这样就将学科成绩引入到了学生的基本信息student
中。
在使用方面也很简单。可以如下使用:
var st student
st.Name = "小明"
st.Sex = "男"
st.Age = 11
st.XueKe.ShuXue = 91
st.XueKe.YuWen = 90
fmt.Printf("%#v\n", st)
也可以这样:
st1 := student{
Name: "小红",
Sex: "女",
Age: 1,
XueKe: xueKe{
ShuXue: 80,
YuWen: 81,
},
}
fmt.Printf("%#v\n", st1)
那么除了上面的写法,我们还有没有其他的写法呢?我们可以使用内嵌。如下所示:
type student struct {
Name string
Sex string
Age int
xueKe
}
在上面的代码中,我们直接将xueKe
写在了student
中,相当于将xueKe
的字段引用进了student
。
我们可以像这样来使用它:
st.ShuXue = 91
st.YuWen = 90
也可以这样:
st1 := student{
Name: "小红",
Sex: "女",
Age: 1,
xueKe: xueKe{
ShuXue: 90,
},
}
需要注意的是,当使用内嵌的时候,如果外部结构体与内嵌进来的结构体有相同的字段,那么优先操作的是外部结构体的字段。
其他特性
结构体还可以实现结构体方法、结构体与接口的结合使用、结构体的tag
、序列化与反序列化,这些内容我们暂时不做讲解,在后续的章节中我们再进行说明。
结构体同样也可以使用指针,与之前讲到的基本类型指针是差不多的。如下所示:
var st2 *student
也可以这样:
st3 := &student{}
结构体也可以实现切片,与其他基本数据类型的用法是一致的。如下所示:
var stSlice []student
当实现切片是,我们同样可以使用for i...
、for range
进行遍历。
小结
结构体在我们实际开发时,主要用途有数据库操作
、HTTP
接口实现、各种通信对接等。
总的来说,结构体是我们用的最多的一种聚合数据类型,我们可以定义任意类型的字段。
关于结构体的总结如下:
通过
type ... struct
进行定义声明及指针与基本数据类型是一致的
在切片中的使用与基本数据类型是一致的
可以使用符号"."来访问结构体的元素,这与其他语言是一样的
可以使用内嵌与嵌套类进行更加复杂的功能设计